
#include <BObject>
#include "IGlobal.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BObject*)_ptr)
#define member_allocate()		_ptr = new member_BObject(this)
#define member_release()		delete (member_BObject*)_ptr

BObject::BObject()
{
	member_allocate();
}
BObject::BObject(const BString& name)
{
	member_allocate();
	member.name = name;
}
BObject::BObject(const BObject& other)
{
	member_allocate();
	member.name = other.name();
}
BObject::~BObject()
{
	member_release();
	free(((ClassNames&)_cns).data);
}

bool BObject::setName(const BString& name)
{
	if (member.name != name)
	{
		member.name = name;
		emit(Signal_Renamed, member.name);
		return true;
	}
	return false;
}
const BString& BObject::name() const
{
	return member.name;
}

bool BObject::setEnabled(bool enabled)
{
	if (member.enabled != enabled)
	{
		member.enabled = enabled;
		emit(Signal_Enabled, member.enabled);
		return true;
	}
	return false;
}
bool BObject::enabled() const
{
	return member.enabled;
}

bool BObject::setAsset(const BValue& asset)
{
	if (member.asset != asset)
	{
		member.asset = asset;
		emit(Signal_AssetChanged, asset);
		return true;
	}
	return false;
}
const BValue& BObject::asset() const
{
	return member.asset;
}

bool BObject::setProperty(const char* name, const BValue& value)
{
	if (!name)
		return false;
	if (value.empty())
	{
		if (member.propertyMap.remove(name))
		{
			emit(Signal_PropertyChanged, BString(name));
		}
		return true;
	}
	auto it = member.propertyMap.insert(name);
	if (*it != value)
	{
		*it = value;
		emit(Signal_PropertyChanged, it.key());
	}
	return true;
}
const BValue& BObject::property(const char* name) const
{
	return member.propertyMap(name);
}

bool BObject::setProperty(const BString& name, const BValue& value)
{
	if (name.empty())
		return false;
	if (value.empty())
	{
		if (member.propertyMap.remove(name))
		{
			emit(Signal_PropertyChanged, name);
		}
		return true;
	}
	auto it = member.propertyMap.insert(name);
	if (*it != value)
	{
		*it = value;
		emit(Signal_PropertyChanged, name);
	}
	return true;
}
const BValue& BObject::property(const BString& name) const
{
	return member.propertyMap(name);
}

bool BObject::typed(const char* type)
{
	for (int i = 0; i < this->typeCount(); i++)
	{
		const char* typeName = this->typeName(i);
		if (0 == strcmp(type, typeName))
		{
			return true;
		}
	}
	return false;
}
bool BObject::typed(const BString& type)
{
	for (int i = 0; i < this->typeCount(); i++)
	{
		const char* typeName = this->typeName(i);
		if (type == typeName)
		{
			return true;
		}
	}
	return false;
}

bool BObject::input(const BString& text, const BValue& value)
{
	if (text == "name")
	{
		const BString& name = value;
		return setName(name);
	}
	if (text == "enabled")
	{
		bool enabled = value;
		return setEnabled(enabled);
	}
	if (text == "asset")
	{
		return setAsset(value);
	}
	return false;
}
bool BObject::query(const BString& text, BValue& value) const
{
	if (text == "name")
	{
		value = member.name;
		return true;
	}
	if (text == "enabled")
	{
		value = member.enabled;
		return true;
	}
	if (text == "asset")
	{
		value = member.propertyMap.def();
		return true;
	}
	return false;
}
const BValue& BObject::query(const BString& text) const
{
	static BValue value;
	query(text, value);
	return value;
}

bool BObject::lock() const
{
	if(member.initMutex())
	{
#ifdef linux
		int error = pthread_mutex_lock(&member.mutex);
		return error == 0;
#else
		EnterCriticalSection(&member.criticalSection);
		return true;
#endif
	}
	return false;
}
bool BObject::unlock() const
{
	if(member.initMutex())
	{
#ifdef linux
		int error = pthread_mutex_unlock(&member.mutex);
		return error == 0;
#else
		LeaveCriticalSection(&member.criticalSection);
		return true;
#endif
	}
	return false;
}
bool BObject::trylock() const
{
	if(member.initMutex())
	{
#ifdef linux
		int error = pthread_mutex_trylock(&member.mutex);
		return error == 0;
#else
		bool ret = (TRUE == TryEnterCriticalSection(&member.criticalSection));
		return ret;
#endif
	}
	return false;
}

bool BObject::wait(int signal, double timeout)
{
#ifdef linux
	pthread_t handle = pthread_self();
#endif
#ifdef _WIN32
	HANDLE handle = GetCurrentThread();
#endif
	bool result = false;
	App_LockGlobalMutex();
	SignalWaitArray& signalWaits = app_SignalWaitMap[this];
	SignalWait& sigwait = signalWaits.append();
	sigwait.handle = handle;
	sigwait.signal = signal;
	sigwait.result = &result;
	sigwait.timeout = bTime() + timeout;
	App_UnlockGlobalMutex();
	bSleep(timeout);
	return result;
}
bool BObject::wait(const char* signal, double timeout)
{
#ifdef linux
	pthread_t handle = pthread_self();
#endif
#ifdef _WIN32
	HANDLE handle = GetCurrentThread();
#endif
	bool result = false;
	App_LockGlobalMutex();
	SignalWaitArray& signalWaits = app_SignalWaitMap[this];
	SignalWait& sigwait = signalWaits.append();
	sigwait.handle = handle;
	sigwait.strsig = signal;
	sigwait.result = &result;
	sigwait.timeout = bTime() + timeout;
	App_UnlockGlobalMutex();
	bSleep(timeout);
	return result;
}
bool BObject::wait(const BString& signal, double timeout)
{
#ifdef linux
	pthread_t handle = pthread_self();
#endif
#ifdef _WIN32
	HANDLE handle = GetCurrentThread();
#endif
	bool result = false;
	App_LockGlobalMutex();
	SignalWaitArray& signalWaits = app_SignalWaitMap[this];
	SignalWait& sigwait = signalWaits.append();
	sigwait.handle = handle;
	sigwait.strsig = signal;
	sigwait.result = &result;
	sigwait.timeout = bTime() + timeout;
	App_UnlockGlobalMutex();
	bSleep(timeout);
	return result;
}

bool BObject::disconnect(int signal)
{
	member_BObject::SigSlotMap& globalSigSlotMap = member.globalSigSlotMap();
	if(signal == 0)
	{
		for (member_BObject::SlotArray& slots : globalSigSlotMap)
		{
			for (int i = 0; i < slots.size(); i++)
			{
				if (slots[i].object == this)
				{
					slots.remove(i--, 1);
				}
			}
		}
	}
	else
	{
		member_BObject::SlotArray& slots = globalSigSlotMap[signal];
		for (int i = 0; i < slots.size(); i++)
		{
			if (slots[i].object == this)
			{
				slots.remove(i--, 1);
			}
		}
	}
	return true;
}
bool BObject::disconnect(const BString& signal)
{
	member_BObject::StrSigSlotMap& globalStrSigSlotMap = member.globalStrSigSlotMap();
	if (signal.empty())
	{
		for (member_BObject::SlotArray& slots : globalStrSigSlotMap)
		{
			for (int i = 0; i < slots.size(); i++)
			{
				if (slots[i].object == this)
				{
					slots.remove(i--, 1);
				}
			}
		}
	}
	else
	{
		member_BObject::SlotArray& slots = globalStrSigSlotMap[signal];
		for (int i = 0; i < slots.size(); i++)
		{
			if (slots[i].object == this)
			{
				slots.remove(i--, 1);
			}
		}
	}
	return true;
}

bool BObject::disconnect(BObject* object)
{
	return disconnect(object, 0) && disconnect(object, BString());
}
bool BObject::disconnect(BObject* object, int signal)
{
	if (!object)
		return false;
	member_BObject::SigSlotMap& sigSlotMap = object_member(object)->sigSlotMap;
	if (signal)
	{
		member_BObject::SlotArray& slots = sigSlotMap[signal];
		for (int i = 0; i < slots.size(); i++)
		{
			if (slots[i].object == this)
			{
				slots.remove(i--, 1);
			}
		}
	}
	else
	{
		for (member_BObject::SlotArray& slots : sigSlotMap)
		{
			for (int i = 0; i < slots.size(); i++)
			{
				if (slots[i].object == this)
				{
					slots.remove(i--, 1);
				}
			}
		}
	}
	return true;
}
bool BObject::disconnect(BObject* object, const BString& signal)
{
	if (!object)
		return false;
	member_BObject::StrSigSlotMap& strSigSlotMap = object_member(object)->strSigSlotMap;
	if (signal.empty())
	{
		for (member_BObject::SlotArray& slots : strSigSlotMap)
		{
			for (int i = 0; i < slots.size(); i++)
			{
				if (slots[i].object == this)
				{
					slots.remove(i--, 1);
				}
			}
		}
	}
	else
	{
		member_BObject::SlotArray& slots = strSigSlotMap[signal];
		for (int i = 0; i < slots.size(); i++)
		{
			if (slots[i].object == this)
			{
				slots.remove(i--, 1);
			}
		}
	}
	return true;
}

bool BObject::emit(int signal, const BValue& value) const
{
	if (signal == 0)
		return false;
	if (app_SignalWaitMap.size())
	{
		App_LockGlobalMutex();
		BReal time = bTime();
		SignalWaitArray& signalWaits = app_SignalWaitMap(this);
		for (int i = 0; signalWaits.size(); i++)
		{
			SignalWait& sigwait = signalWaits[i];
			if (time > sigwait.timeout)
			{
				signalWaits.remove(i--);
				continue;
			}
			if (sigwait.signal == signal)
			{
				*sigwait.result = true;
			#ifdef linux
				pthread_cond_signal(&sigwait.cond);
			#else
				ResumeThread(sigwait.handle);
			#endif
				signalWaits.remove(i--);
			}
		}
		if (signalWaits.empty())
			app_SignalWaitMap.remove(this);
		App_UnlockGlobalMutex();
	}
	member_BObject::SlotArray& slots = member.sigSlotMap(signal);
	for (int i = 0; i < slots.size(); i++)
	{
		member_BObject::Slot& slot = slots[i];
		slot(this, value);
	}
	member_BObject::SigSlotMap& globalSigSlotMap = member.globalSigSlotMap();
	member_BObject::SlotArray& globalSlots = globalSigSlotMap(signal);
	for (int i = 0; i < globalSlots.size(); i++)
	{
		member_BObject::Slot& slot = globalSlots[i];
		slot(this, value);
	}
	return true;
}
bool BObject::emit(const BString& signal, const BValue& value) const
{
	if (signal.empty())
		return 0;
	if (app_SignalWaitMap.size())
	{
		App_LockGlobalMutex();
		BReal time = bTime();
		SignalWaitArray& signalWaits = app_SignalWaitMap(this);
		for (int i = 0; signalWaits.size(); i++)
		{
			SignalWait& sigwait = signalWaits[i];
			if (time > sigwait.timeout)
			{
				signalWaits.remove(i--);
				continue;
			}
			if (sigwait.strsig == signal)
			{
				*sigwait.result = true;
			#ifdef linux
				pthread_cond_signal(&sigwait.cond);
			#else
				ResumeThread(sigwait.handle);
			#endif
				signalWaits.remove(i--);
			}
		}
		if (signalWaits.empty())
			app_SignalWaitMap.remove(this);
		App_UnlockGlobalMutex();
	}
	member_BObject::SlotArray& slots = member.strSigSlotMap(signal);
	for (int i = 0; i < slots.size(); i++)
	{
		member_BObject::Slot& slot = slots[i];
		slot(this, value);
	}
	member_BObject::StrSigSlotMap& globalStrSigSlotMap = member.globalStrSigSlotMap();
	member_BObject::SlotArray& globalSlots = globalStrSigSlotMap(signal);
	for (int i = 0; i < globalSlots.size(); i++)
	{
		member_BObject::Slot& slot = globalSlots[i];
		slot(this, value);
	}
	return true;
}

bool BObject::post(int signal, const BValue& value) const
{
	if (signal == 0)
		return false;
	App_LockGlobalMutex();
	SlotPackArray& slotPacks = app_slotPackMap[(BObject*)this];
	slotPacks.append(SlotPack(signal, value));
	App_UnlockGlobalMutex();
	return true;
}
bool BObject::post(const BString& signal, const BValue& value) const
{
	if (signal.empty())
		return false;
	App_LockGlobalMutex();
	SlotPackArray& slotPacks = app_slotPackMap[(BObject*)this];
	slotPacks.append(SlotPack(signal, value));
	App_UnlockGlobalMutex();
	return true;
}

bool BObject::post(BObject* receiver, int signal, const BValue& value) const
{
	if (signal == 0)
		return false;
	App_LockGlobalMutex();
	SlotPackArray& slotPacks = app_slotPackMap[(BObject*)this];
	slotPacks.append(SlotPack(receiver, signal, value));
	App_UnlockGlobalMutex();
	return true;
}
bool BObject::post(BObject* receiver, const BString& signal, const BValue& value) const
{
	if (signal.empty())
		return false;
	App_LockGlobalMutex();
	SlotPackArray& slotPacks = app_slotPackMap[(BObject*)this];
	slotPacks.append(SlotPack(receiver, signal, value));
	App_UnlockGlobalMutex();
	return true;
}

bool BObject::attach(int signal, slot method)
{
	if (signal == 0)
		return false;
	member_BObject::Slot temp(this, method);
	member_BObject::SigSlotMap& globalSigSlotMap = member.globalSigSlotMap();
	member_BObject::SlotArray& slots = globalSigSlotMap[signal];
	slots.append(temp);
	return true;
}
bool BObject::attach(BObject* object, int signal, slot method)
{
	if (!object || signal == 0)
		return false;
	member_BObject::Slot temp(this, method);
	member_BObject::SlotArray& slots = object_member(object)->sigSlotMap[signal];
	if (!slots.contain(temp))
		slots.append(temp);
	return true;
}
bool BObject::attach(const BString& signal, slot method)
{
	if (signal.empty())
		return false;
	member_BObject::Slot temp(this, method);
	member_BObject::StrSigSlotMap& globalStrSigSlotMap = member.globalStrSigSlotMap();
	member_BObject::SlotArray& slots = globalStrSigSlotMap[signal];
	if (!slots.contain(temp))
		slots.append(temp);
	return true;
}
bool BObject::attach(BObject* object, const BString& signal, slot method)
{
	if (!object || signal.empty())
		return false;
	member_BObject::Slot temp(this, method);
	member_BObject::SlotArray& slots = object_member(object)->strSigSlotMap[signal];
	if (!slots.contain(temp))
		slots.append(temp);
	return true;
}

bool BObject::checks(int signal, slot method)
{
	if (signal == 0)
		return false;
	member_BObject::SigSlotMap& globalSigSlotMap = member.globalSigSlotMap();
	member_BObject::SlotArray& globalSlots = globalSigSlotMap(signal);
	for (int i = 0; i < globalSlots.size(); i++)
	{
		member_BObject::Slot& slot = globalSlots[i];
		if (slot.object != this)
			continue;
		if (method && slot.method != method)
			continue;
		return true;
	}
	return false;
}
bool BObject::checks(BObject* object, int signal, slot method)
{
	if (!object || signal == 0)
		return false;
	member_BObject::SlotArray& slots = object_member(object)->sigSlotMap[signal];
	for (int i = 0; i < slots.size(); i++)
	{
		member_BObject::Slot& slot = slots[i];
		if (slot.object != this)
			continue;
		if (method && slot.method != method)
			continue;
		return true;
	}
	return false;
}
bool BObject::checks(const BString& signal, slot method)
{
	if (signal.empty())
		return false;
	member_BObject::StrSigSlotMap& globalStrSigSlotMap = member.globalStrSigSlotMap();
	member_BObject::SlotArray& globalSlots = globalStrSigSlotMap(signal);
	for (int i = 0; i < globalSlots.size(); i++)
	{
		member_BObject::Slot& slot = globalSlots[i];
		if (slot.object != this)
			continue;
		if (method && slot.method != method)
			continue;
		return true;
	}
	return false;
}
bool BObject::checks(BObject* object, const BString& signal, slot method)
{
	if (!object || signal.empty())
		return false;
	member_BObject::SlotArray& slots = object_member(object)->strSigSlotMap[signal];
	for (int i = 0; i < slots.size(); i++)
	{
		member_BObject::Slot& slot = slots[i];
		if (slot.object != this)
			continue;
		if (method && slot.method != method)
			continue;
		return true;
	}
	return false;
}

